package cumeo89.chii.client.mw;

import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.http.client.RequestBuilder.Method;
import com.google.gwt.xml.client.Document;
import com.google.gwt.xml.client.Element;
import com.google.gwt.xml.client.XMLParser;

import cumeo89.chii.client.util.AsyncCallback;
import cumeo89.chii.client.util.JSStringArray;
import cumeo89.chii.client.util.StringUtil;

public abstract class MediaWiki {

	private String base;

	public MediaWiki(String base) {
		this.base = base;
	}

	public String getBase() {
		return base;
	}

	private void sendRequest(String parameters, RequestCallback callback)
			throws RequestException {
		sendRequest(parameters, callback, RequestBuilder.GET);
	}

	private void sendRequest(String parameters, RequestCallback callback,
			Method method) throws RequestException {
		RequestBuilder rb = null;
		if (method == RequestBuilder.POST) {
			rb = new RequestBuilder(method, base + "api.php");
			rb.setHeader("Content-Type",
					"application/x-www-form-urlencoded; charset=UTF-8");
			rb.setRequestData(parameters + "&format=xml");
		} else if (method == RequestBuilder.GET) {
			rb = new RequestBuilder(method, base + "api.php?format=xml&"
					+ parameters);
			rb.setRequestData(null);
		} else {
			throw new IllegalArgumentException();
		}
		rb.setCallback(callback);
		rb.send();
	}

	public void getCategories(final String prefix,
			final AsyncCallback<String[]> callback) {
		try {
			sendRequest(
					"action=query&list=allpages&apfilterredir=nonredirects&"
							+ "apnamespace=14&apfrom=" + URL.encode(prefix),
					new RequestCallback() {

						public void onError(Request request, Throwable exception) {
							callback.onFailure(exception);
						}

						public void onResponseReceived(Request httpRequest,
								Response httpResponse) {
							JSStringArray a = StringUtil.extract(httpResponse
									.getText(), "title=\"Thể loại:([^\"]+)\"");
							String[] r = new String[a.length() - 1];
							for (int i = 1; i < a.length(); i++) {
								r[i - 1] = a.get(i);
							}
							callback.onSuccess(r);
						}

					});
		} catch (RequestException e) {
			callback.onFailure(e);
		}
	}

	public void edit(String title, final AsyncCallback<Page> callback) {
		try {
			sendRequest(
					"action=query&prop=info|revisions&rvprop=timestamp|content&"
							+ "intoken=edit&titles=" + title,
					new RequestCallback() {

						public void onError(Request request, Throwable exception) {
							callback.onFailure(exception);
						}

						public void onResponseReceived(Request request,
								Response response) {
							Document doc = XMLParser.parse(response.getText());

							if (doc.getElementsByTagName("error").getLength() > 0) {
								Element error = (Element) doc
										.getElementsByTagName("error").item(0);
								callback.onFailure(new MediaWikiAPIException(
										error.getAttribute("code"), error
												.getAttribute("info")));
								return;
							}

							Element page = (Element) doc.getElementsByTagName(
									"page").item(0);
							Page ret = new Page(page.getAttribute("title"));
							ret.setEditToken(page.getAttribute("edittoken"));
							if (!page.hasAttribute("missing")) {
								Element rev = (Element) doc
										.getElementsByTagName("rev").item(0);
								ret.setBaseTimeStamp(rev
										.getAttribute("timestamp"));
								ret.setContent(rev.getFirstChild()
										.getNodeValue());
							}
							callback.onSuccess(ret);
						}

					});
		} catch (RequestException e) {
			callback.onFailure(e);
		}
	}

	public void save(final Page page, final AsyncCallback<Void> callback) {
		try {
			StringBuilder sb = new StringBuilder("action=edit");
			sb.append("&title=").append(page.getTitle());
			sb.append("&text=").append(page.getContent());
			sb.append("&token=").append(
					URL.encodeComponent(page.getEditToken()));
			sb.append("&summary=").append(page.getSummary());
			if (page.getMinor() != null) {
				sb.append(page.getMinor() ? "&minor" : "&notminor");
			}
			if (page.getBaseTimeStamp() != null) {
				sb.append("&basetimestamp=").append(page.getBaseTimeStamp());
			}
			if (page.getWatch() != null) {
				sb.append(page.getWatch() ? "&watch" : "&unwatch");
			}
			if (page.getCaptchaId() != null) {
				sb.append("&captchaid=").append(page.getCaptchaId());
				sb.append("&captchaword=").append(page.getCaptchaWord());
			}
			sendRequest(sb.toString(), new RequestCallback() {

				public void onError(Request request, Throwable exception) {
					callback.onFailure(exception);
				}

				public void onResponseReceived(Request request,
						Response response) {
					Document doc = XMLParser.parse(response.getText());

					if (doc.getElementsByTagName("error").getLength() > 0) {
						Element error = (Element) doc.getElementsByTagName(
								"error").item(0);
						callback.onFailure(new MediaWikiAPIException(error
								.getAttribute("code"), error
								.getAttribute("info")));
						return;
					}

					if (doc.getElementsByTagName("captcha").getLength() > 0) {
						Element captcha = (Element) doc.getElementsByTagName(
								"captcha").item(0);
						page.setCaptchaType(captcha.getAttribute("type"));
						page.setCaptchaMime(captcha.getAttribute("mime"));
						page.setCaptchaId(captcha.getAttribute("id"));
						if (captcha.hasAttribute("question")) {
							page.setCaptchaQuestion(captcha
									.getAttribute("question"));
						}
						if (captcha.hasAttribute("url")) {
							page.setCaptchaURL(captcha.getAttribute("url"));
						}
						callback.onFailure(new CaptchaException());
						return;
					}

					Element edit = (Element) doc.getElementsByTagName("edit")
							.item(0);
					if ("Failure".equalsIgnoreCase(edit.getAttribute("result"))) {
						callback.onFailure(null);
						return;
					}

					callback.onSuccess(null);
				}

			}, RequestBuilder.POST);
		} catch (RequestException e) {
			callback.onFailure(e);
		}
	}

	public void preview(Page page, final AsyncCallback<String> callback) {
		try {
			StringBuilder sb = new StringBuilder("action=parse&prop=text");
			sb.append("&title=").append(page.getTitle());
			sb.append("&text=").append(page.getContent());
			sendRequest(sb.toString(), new RequestCallback() {

				public void onError(Request request, Throwable exception) {
					callback.onFailure(exception);
				}

				public void onResponseReceived(Request request,
						Response response) {
					Document doc = XMLParser.parse(response.getText());
					Element text = (Element) doc.getElementsByTagName("text")
							.item(0);
					if (text != null) {
						callback.onSuccess(text.getFirstChild().getNodeValue());
					} else {
						callback.onFailure(null);
					}
				}

			}, RequestBuilder.POST);
		} catch (RequestException e) {
			callback.onFailure(e);
		}
	}

	public abstract Document parse(String title, String wikitext);

	public abstract String toWikitext(Document doc);

	public abstract String toHtml(String wikitext);

}